/*
 * 
 * */
package systemsim;

import java.util.*;

/**
 * The restaurant:
 * keeps track of restaurant capacity and fill levels
 * keeps track of which groups sit at which tables
 * keeps track of groups' status (ie waiting for food / eating)
 * sends food requests to kitchen
 * retrieves group orders which are complete from the kitchen
 * @author emil
 */
public class Restaurant {
    
    private int totNumTables;
    private int numSeatsPerTable;
    private Table[] tables;         // The tables in the restaurant
    private Kitchen kitchen;

    private GUITables guiTables;
    
    //This is a lookup hashmap, to find the tables which a group occupies.
    private HashMap<Group, ArrayList<Integer>> seatingChart = new HashMap<Group, ArrayList<Integer>>();
    
    //This is a set of all the groups which are currently inside the restaurant
    //private HashSet<Group> groups = new HashSet<Group>();
    
    
    
    /*Background Daemon process which will handle restaurant functions:
     * keep track of restaurant capacity and fill levels
     * keep track of which groups sit at which tables
     * keep track of groups' status (ie waiting for food / eating)
     * sends food requests to kitchen
     * retieves group orders which are complete from the kitchen
    */
    public static Timer restaurantProcess = new Timer();
    
    
    
            
    //===========================================//
    //===========================================//
    
    public Restaurant(Kitchen kitchen, int totNumOfTables, int numOfSeatsPerTable)
    {
        this.kitchen = kitchen;
        //this.totNumGuests = totNumofGuests;
        this.totNumTables = totNumOfTables;
        this.numSeatsPerTable = numOfSeatsPerTable;
        tables = new Table[totNumOfTables];
        
        //initialize the Tables in the restaurant
        for(int i = 0; i < totNumOfTables; i++)
        {
            tables[i] = new Table(i, false);
        }

        guiTables = new GUITables(totNumTables);
        
        restaurantProcess.schedule(new RestaurantTask(),100,100);
    }

   
    /**checks to see if a table is available
     * @param numOfTables the number of tables which are required
     * @return returns a set of tables (int[] which holds the table IDs) if the requested number of tables is available.
     * Otherwise, returns null 
     */
    private synchronized ArrayList<Integer> requestTables(int numOfTables)
    {
        int numOfAvailableTablesFound = 0;
        ArrayList<Integer> requestedTables = new ArrayList<Integer>();
        
        for(int i = 0; i < tables.length; i++)
        {
            if(!tables[i].isOccupied())
            {
                requestedTables.add(i);
                numOfAvailableTablesFound++;
                
                if(numOfAvailableTablesFound >= numOfTables)
                {
                    return requestedTables;
                }
            }
        }
        
        return null;
    }
    
    /**Attempt to sit the group in the restaurant.
     @param group the group to sit
     @return returns true if group was seated successfully, false otherwise(this could be due to full restaurant for ex.)
     */
    public boolean attemptToSitCustomers(Group group)
    {
        synchronized(Main.restaurantLock)
        {
            System.out.println("Restaurant AttemptTosit Group"+group);
            ArrayList<Integer> tablesForGroup = requestTables(group.getNumTables());

            //IF there are enough available tables for this group, go ahead and sit them
            if(tablesForGroup != null)
            {
                //indicate that the group is now seated
                group.state = Group.Status.SEATED;
                
                //add the group to the restaurant
                //groups.add(group);

                //update the seating chart
                seatingChart.put(group, tablesForGroup);    

                //Mark the tables as occupied
                Iterator iter = tablesForGroup.iterator();  
                while(iter.hasNext())
                {
                    tables[(Integer)(iter.next())].setOccupied(true);
                }
                guiTables.addTable(group);
                System.out.println("Restaurant success, seated Group"+group);

               /* int counter = 0;
                for(int i = 0; i < tables.length; i++)
                {
                    if(!tables[i].isOccupied())
                    {
                        counter++;
                    }
                }
                System.out.println("                 " + counter);*/

                return true;
            }
            else
            {
                System.out.println("Restaurant fail to sit Group"+group);
                return false;
            }
       }
    }
    
    
    
    
    public int getNumSeatsPerTable() {
        return numSeatsPerTable;
    }

    public void setNumSeatsPerTable(int numSeatsPerTable) {
        this.numSeatsPerTable = numSeatsPerTable;
    }


    public int getTotNumTables() {
        return totNumTables;
    }

    public void setTotNumTables(int totNumTables) {
        this.totNumTables = totNumTables;
    }
    
    
    private class RestaurantTask extends java.util.TimerTask
    {
        public void run()
        {
            //System.out.println("Restaurant Run");
            
            synchronized(Main.restaurantLock)
            {
                
                //System.out.println("Restaurant Run Inside Sync1");
                
                long currTime = System.currentTimeMillis();
                
                
                //Check for any Orders which are ready from the kithcen and serve them to the appropriate groups
                Group togetFood = kitchen.getPreparedFood();    //The group whose food is ready
                
                while(togetFood != null)
                {
                    
                    //System.out.println("Restaurant Run Inside Sync2");
                    
                    
                    if(!seatingChart.containsKey(togetFood))        //If the food we just received does not match any group, we have a problem.
                    {
                        System.out.println("Error, food from kitchen has no corresponding group in restaurant!");
                        System.out.println(togetFood);
                    }
                    
                    togetFood.state = Group.Status.EATING;          //Set the group's status to eating
                    togetFood.setTimeWhenFoodReceived(currTime);
                    guiTables.updateState(togetFood);
                                        
                    System.out.println("Group "+togetFood+" is now Eating.");
                    
                    togetFood = kitchen.getPreparedFood();
                }
                
                //=================================================//
                //=================================================//
                
                
                Iterator<Map.Entry<Group, ArrayList<Integer>>> iter = seatingChart.entrySet().iterator();

                //While there are more people
                while(iter.hasNext())
               {
                   //System.out.println("Restaurant Run Inside Sync3");
                   
                   Group currentGroup = iter.next().getKey();
                   if(currentGroup == null)
                   {
                       System.out.println("ERROR -- NULL in iterator of seatingChart in Restaurant");
                   }
                   
                   //If Group is seated, send their order to the kitchen
                   if(currentGroup.state == Group.Status.SEATED)
                   {
                       kitchen.addOrder(currentGroup);
                       currentGroup.state = Group.Status.WAITINGONFOOD;
                       guiTables.updateState(currentGroup);
                       
                       
                       
                       System.out.println("Group "+currentGroup+" has bee seated and their order has been taken - WAITINGFORFOOD.");
                       
                   }
                   
                   //If a group is eating, check to see if they are ready to leave
                   //-> they leave if the time at which they arrived + the time assigned to them is >= than current time
                   if(currentGroup.state == Group.Status.EATING)
                   {
                       //System.out.println(currentGroup.timeWhenFoodReceived - currTime);
                       //System.out.println(currentGroup.getTimeOfDepartureMilliseconds() - currTime);
                       if(currentGroup.getTimeOfDepartureMilliseconds() <= currTime)
                       {
                           //Group is done and will leave
                           //remove group from restaurant list and free the tables
                            
                           ArrayList<Integer> groupTables = seatingChart.get(currentGroup);
                                                      
                           if(groupTables == null)
                           {
                               System.out.println("Error removing group from the restaurant");
                           }
                           
                           //mark their table(s) as free
                           for(int i = 0; i<groupTables.size(); i++)
                           {
                               tables[groupTables.get(i)].setOccupied(false);
                           }
                           
                           iter.remove();   // Remove the group from the seating chart
                                            //This is done through the iterator to avoid 
                                            //concurrency issues

                           guiTables.removeTable(currentGroup);

                           System.out.println("Group "+currentGroup+" has LEFT");
                           
                       }
                   }
                   
                   
                   
                   
               }
           }
            
        }
    }
    
    public static void main (String[] args)
    {
        //ArrayList<Integer> r = new ArrayList<Integer>();
        //r.
    }
}
